#ifndef SIMPLE_SUPPORT_ITERATOR_OUT_INVOKE_HPP
#define SIMPLE_SUPPORT_ITERATOR_OUT_INVOKE_HPP

#include <iterator> // std::output_iterator_tag
#include <type_traits> // std::enable_if_t std::is_same_v
#include <utility> // std::forward

#include "../function_utils.hpp" // support::invoke

namespace simple::support
{

	template <typename Function>
	struct out_invoke
	{
		using value_type = void;
		using difference_type = void;
		using pointer = void;
		using reference = void;
		using iterator_category = std::output_iterator_tag;
		Function function;

		template <typename F,
			std::enable_if_t<std::is_same_v<F,Function>>* = nullptr>
		explicit out_invoke(F&& function)
			: function(std::forward<F>(function))
		{}

		out_invoke& operator++() { return *this; }
		out_invoke operator++(int) { out_invoke prev = *this; ++(*this); return prev; }
		out_invoke& operator--() { return *this; }
		out_invoke operator--(int) { out_invoke prev = *this; --(*this); return prev; }

		class proxy
		{
			Function& function;

			proxy(Function& function) : function(function) {}
			friend struct out_invoke;
			public:
			proxy() = delete;
			proxy(const proxy&) = delete;
			proxy(const proxy&&) = delete;
			proxy& operator=(const proxy&) = delete;
			proxy& operator=(const proxy&&) = delete;

			template <typename T>
			const proxy& operator=(T&& x) const
			{
				support::invoke(function, std::forward<T>(x));
				return *this;
			}

		};

		const proxy operator*()
		{
			return {function};
		}
	};
	template <typename F> out_invoke(F&&) -> out_invoke<F>;

} // namespace simple::support

#endif /* end of include guard */
